/*
 * Copyright (c) 2012 Robert Burrell Donkin robertburrelldonkin.name
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
 * and associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software 
 * is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all copies or 
 * substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 
 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * 
 */
package sekhmet.audio.source.mic;

import java.util.concurrent.BlockingQueue;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.Line;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.TargetDataLine;

import sekhmet.audio.data.SignalData;
import sekhmet.audio.format.AudioFormatWrangler;
import sekhmet.audio.source.AbstractSource;
import sekhmet.audio.source.AudioReaderSource;


public class Microphone {

	private final DataLine.Info lineInfo;
	private final Mixer.Info mixerInfo;
	private TargetDataLine line;
	private AbstractSource source;
		
	public Microphone(DataLine.Info lineInfo, javax.sound.sampled.Mixer.Info mixerInfo) {
		super();
		this.lineInfo = lineInfo;
		this.mixerInfo = mixerInfo;
	}
	
	public Line.Info getLineInfo() {
		return lineInfo;
	}

	public Mixer.Info getMixerInfo() {
		return mixerInfo;
	}

	public AudioFormat getLineFormat() {
		if (line == null) {
			return null;
		}
		return line.getFormat();
	}
	
	@Override
	public String toString() {
		return "Microphone [lineInfo=" + lineInfo + ", mixerInfo=" + mixerInfo
				+ ", active=" + dumpActiveLineInfo() +"]";
	}

	public synchronized Microphone open() throws LineUnavailableException {
		if (line == null) {
			final Line systemLine = AudioSystem.getMixer(mixerInfo).getLine(lineInfo);
			if (systemLine instanceof TargetDataLine) {
				line = (TargetDataLine) systemLine;
			} else {
				throw new LineUnavailableException("Expected line to be a microphone.");
			}
		}
		
		if (line.isOpen()) {
			System.out.println("Already open.");
		} else {
			line.open(new AudioFormat(96000f, 24, 1, true, true), lineInfo.getMaxBufferSize());
		}
		return this;
	}
	
	public String dumpActiveLineInfo() {
		if (line == null) {
			return "NONE";
		}
		return "buffer: " + line.getBufferSize() + "; encoding:" + line.getFormat().getEncoding() 
		 + "; sample rate:" + line.getFormat().getSampleRate() 
		  + "; frame size:" + line.getFormat().getFrameSize()  
		   + "; frame rate:" + line.getFormat().getFrameRate()
		    + "; channels:" + line.getFormat().getChannels();
	}
	
	public synchronized Microphone close() {
		if (line != null && line.isOpen()) {
			line.close();
			line = null;
		}
		if (source != null) {
			source.stop();
		}
		return this;
	}

	public Microphone queueTo(BlockingQueue<SignalData> queue) {
		if (source != null) {
			source.stop();
		}
		source = new AudioReaderSource(queue, new AudioFormatWrangler().read(line));
		return this;
	}

	public Microphone startRecording() {
		new Thread(source).start();
		return this;
	}
}
